home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d16
/
winvn060.arc
/
WVBLOCK.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-07-01
|
46KB
|
1,288 lines
/* -- WVBLOCK.C --------------------------------------------------
*
* This file contains a collection of routines to manipulate textblocks
* and lines within textblocks.
* The routines here view lines as atomic units only (they don't
* look at the actual data in the lines).
*
* Mark Riordan 20 September 1989.
*/
#include "windows.h"
#include "wvglob.h"
#include "winvn.h"
#include <ctype.h>
/*-- function NewBlock ------------------------------------------
*
* Creates an empty, new textblock and links it into the list
* of blocks after a given block.
* After the call, both the old ("given") block and the new block
* are locked in memory.
*
* Entry CurBlockPtr points to a block
*
* Exit NewBlockPtr points to a block that has been linked
* into the list just after CurBlockPtr.
* Returns TRUE if couldn't allocate a block, else FALSE.
* (I know I should fix that.)
*/
int
NewBlock(CurBlockPtr,NewBlockPtr)
TypBlock far *CurBlockPtr, far **NewBlockPtr;
{
HANDLE hMyBlock;
TypBlock far *MyBlock, far *MyNextBlock;
hMyBlock = GlobalAlloc(GMEM_MOVEABLE,(long)(CurBlockPtr->OwnerDoc->BlockSize));
if(hMyBlock) {
MyBlock = (TypBlock far *) GlobalLock(hMyBlock);
SetupEmptyBlock(MyBlock,hMyBlock,CurBlockPtr->hCurBlock,
CurBlockPtr->hNextBlock,CurBlockPtr->OwnerDoc);
CurBlockPtr->hNextBlock = hMyBlock;
/* Change the next block's "previous" pointer to point to us. */
if(MyBlock->hNextBlock) {
MyNextBlock = (TypBlock far *) GlobalLock(MyBlock->hNextBlock);
MyNextBlock->hPrevBlock = hMyBlock;
GlobalUnlock(MyBlock->hNextBlock);
}
*NewBlockPtr = MyBlock;
} else {
MessageBox(CurBlockPtr->OwnerDoc->hDocWnd,"Could not allocate textblock","Out of Memory Error",MB_OK|MB_ICONHAND);
return(1);
}
return(0);
}
/*-- function SetupEmptyBlock -----------------------------------------
*
* Initialize fields in a newly-allocated textblock.
* Set the fields to indicate an empty block.
*/
void
SetupEmptyBlock(BlockPtr,hCur,hPrev,hNext,DocPtr)
TypBlock far *BlockPtr;
HANDLE hCur, hPrev, hNext;
TypDoc *DocPtr;
{
BlockPtr->OwnerDoc = DocPtr;
BlockPtr->hCurBlock = hCur;
BlockPtr->hPrevBlock = hPrev;
BlockPtr->hNextBlock = hNext;
BlockPtr->LWAp1 = sizeof(TypBlock) + sizeof(TypLine);
BlockPtr->NumLines = 0;
BlockPtr->eob = END_OF_BLOCK;
#if 0
*((int far *) ((char far *)BlockPtr+sizeof(TypBlock)) ) = END_OF_BLOCK;
#else
((TypLine far *) ((char far *)BlockPtr+sizeof(TypBlock)) )->length = END_OF_BLOCK;
((TypLine far *) ((char far *)BlockPtr+sizeof(TypBlock)) )->LineID = NextLineID++;
#endif
}
/*-- function DeleteBlock ---------------------------------------------
*
* Delete a textblock from a document.
*
* Entry CurBlockPtr points to the block to delete.
*
* Exit CurBlockPtr points to the next block, if any, else the
* previous block.
* CurLinePtr points to the first line of the next block
* if there was one, else the last line of
* the previous block.
*/
BOOL
DeleteBlock(TypBlock far **CurBlockPtr, TypLine far **CurLinePtr)
{
TypBlock far *MyBlockPtr = *CurBlockPtr;
TypBlock far *BlockPtr;
HANDLE hMyBlock = MyBlockPtr->hCurBlock,
hMyPrev = MyBlockPtr->hPrevBlock,
hMyNext = MyBlockPtr->hNextBlock;
BOOL set_cur_block = FALSE;
/* Don't delete the only block in the document. */
if(!hMyNext && !hMyPrev) return(FALSE);
/* Update the prev pointer of the next block to point to
* the block previous to the one being deleted.
*/
if(hMyNext) {
BlockPtr = (TypBlock far *) GlobalLock(hMyNext);
BlockPtr->hPrevBlock = hMyPrev;
GlobalUnlock(hMyNext);
NextLine(CurBlockPtr,CurLinePtr);
set_cur_block = TRUE;
} else {
/* The block we are deleting has no next block, so it
* must be the last in the document. Update document pointers.
*/
MyBlockPtr->OwnerDoc->hLastBlock = hMyPrev;
}
/* Update the next pointer of the previous block to point to
* the block after the one being deleted.
*/
if(hMyPrev) {
BlockPtr = (TypBlock far *) GlobalLock(hMyPrev);
BlockPtr->hNextBlock = hMyNext;
GlobalUnlock(hMyPrev);
if(!set_cur_block) {
/* There is no next block, so we want to position the
* pointer to the end of the previous block.
*/
PrevLine(CurBlockPtr,CurLinePtr);
NextLine(CurBlockPtr,CurLinePtr);
}
} else {
/* The block we are deleting has no previous block, so it
* must be the first in the document. Update document pointers.
*/
MyBlockPtr->OwnerDoc->hFirstBlock = hMyNext;
}
GlobalFree(hMyBlock);
return(TRUE);
}
/*-- function AddLine -------------------------------------------------
*
* Add a line to a textblock. Create a new textblock if necessary.
*
* Entry LineToAdd points to a line to add.
* CurBlockPtr points to the block to which we want to add it.
* CurAddPtr points to the place in the block to add the line.
*
* Exit CurBlockPtr & CurAddPtr point to right after the
* newly-added line.
*
* Method There are three cases:
* 1. The new line will fit in the textblock.
* That's pretty easy.
* 2. The new line won't fit, and we are at the end of
* the current block.
* I could have just treated this as I do case 3 (splitting
* the textblock), but that would have resulted in
* a lot of unnecessary internal fragmentation
* (i.e., wasted space inside textblocks).
* So, what I do is leave the contents of the old block
* alone and just create a new empty block, to which
* the line is added by a recursive call.
* 3. The new line won't fit, and we are not at the end of
* the textblock.
* In this case, I split the textblock at the first line
* boundary after the split point (the split point is
* an attribute of the document--usually about 2/3 of the
* way through the textblock), creating two semi-full textblocks
* where there was one full one before.
* The routine then recursively calls itself.
*/
int
AddLine(LineToAdd,CurBlockPtr,CurAddPtr)
TypLine *LineToAdd;
TypBlock far **CurBlockPtr;
TypLine far **CurAddPtr;
{
TypBlock far *MyBlock = *CurBlockPtr;
int left = (MyBlock->OwnerDoc->BlockSize - MyBlock->LWAp1);
if(LineToAdd->length <= ( int)left ) {
/* There's room in the current block for this line, so just */
/* move lines down to accomodate this line and copy it in. */
MoveBytes((char far *) *CurAddPtr,
((char far *) *CurAddPtr)+LineToAdd->length,
(((char far *)MyBlock+MyBlock->LWAp1)) - (char far *) *CurAddPtr);
MoveBytes((char far *)LineToAdd,(char far *) *CurAddPtr,
LineToAdd->length);
/* Adjust textblock counters. */
MyBlock->LWAp1 += LineToAdd->length;
MyBlock->NumLines++;
IncPtr((*CurAddPtr),LineToAdd->length);
MyBlock->OwnerDoc->TotalLines++;
} else {
/* There isn't enough room in the current textblock. */
/* If we are at the end of the current block, just create */
/* a new empty one; otherwise, split the current block. */
TypBlock far *NewBlockPtr;
if((*CurAddPtr)->length == END_OF_BLOCK) {
/* We're at end of block; create a new empty one. */
/* This will be the current block, so release references */
/* to the now-current block */
if(NewBlock(MyBlock,CurBlockPtr)) {
return(1);
} else {
GlobalUnlock(MyBlock->hCurBlock);
*CurAddPtr = (TypLine far *)
((char far *)*CurBlockPtr + sizeof(TypBlock));
/* Now we can recursively try again to add the line. */
if(AddLine(LineToAdd,CurBlockPtr,CurAddPtr)) {
return(1);
}
}
} else {
/* We need to split the textblock. */
/* Find a place to split the block by starting at the */
/* beginning of the block and skipping through the lines */
/* until we pass the number of bytes that marks the */
/* split point. The previous line is the split point. */
TypLine far *MyLinePtr = (TypLine far *)((char far *)MyBlock+sizeof(TypBlock));
TypLine far *MyLastLine = MyLinePtr;
int nOldLines = 0, nBytesMoved, MyAddOffset;
while((char far *)MyLinePtr - (char far *) MyBlock < MyBlock->OwnerDoc->SplitSize &&
MyLinePtr->length != END_OF_BLOCK) {
nOldLines++;
MyLastLine = MyLinePtr;
IncPtr(MyLinePtr,MyLinePtr->length);
}
/* Allocate the new block and copy the last portion of */
/* the current block to the new one. The range to */
/* copy starts at the above-determined split point and */
/* goes until the LWA+1. */
/* Then adjust the new & old textblock fields. */
if(NewBlock(MyBlock,&NewBlockPtr)) {
return(1);
} else {
MoveBytes(MyLastLine,(char far *)NewBlockPtr+sizeof(TypBlock),
nBytesMoved = (((char far *)MyBlock + MyBlock->LWAp1)) - (char far *)MyLastLine);
MyBlock->LWAp1 = (char far *)MyLastLine - ((char far *)MyBlock)
+ sizeof(TypLine);
((TypLine far *)MyLastLine)->length = END_OF_BLOCK;
((TypLine far *)MyLastLine)->LineID = NextLineID++;
NewBlockPtr->NumLines = MyBlock->NumLines - nOldLines;
MyBlock->NumLines = nOldLines;
NewBlockPtr->LWAp1 = nBytesMoved + sizeof(TypBlock);
/* Should the new line go in the old block or the new? */
/* If the add point is beyond the end of the newly- */
/* truncated block, we must move the add point to the */
/* next block and make the new block the current one. */
/* The new position should be the same */
/* number of line bytes past the beginning of the next */
/* block as it was past the split point when it was in */
/* the old block. */
/* Either way, one block (the old or the new one) */
/* must be unlocked. */
if(*CurAddPtr >= MyLastLine) {
/* Add point is in new block. */
MyAddOffset = (char far *) *CurAddPtr - (char far *)MyLastLine + sizeof(TypBlock);
*CurAddPtr = (TypLine far *)((char far *)NewBlockPtr + MyAddOffset);
*CurBlockPtr = NewBlockPtr;
GlobalUnlock(MyBlock->hCurBlock);
} else {
/* Add point is in current block. */
GlobalUnlock(NewBlockPtr->hCurBlock);
}
return(AddLine(LineToAdd,CurBlockPtr,CurAddPtr));
}
}
}
return(0);
}
/*-- function ReplaceLine -------------------------------------------------
*
* Replace a line in a textblock. Create a new textblock if necessary.
*
* Entry LineToAdd is the line to put into a textblock.
* CurBlockPtr points to the block containing the old copy.
* CurLinePtr points to the old copy of the line.
*
* Exit returns TRUE if successful.
* CurBlockPtr points to the block containing the new copy of the line.
* CurLinePtr points to the new copy of the line.
* Usually, CurBlockPtr & CurAddPtr will be the
* same upon exit as upon entry; however, sometimes
* a textblock split is necessary.
*
* Method There are two cases:
* 1. There is enough room in this textblock for the
* changed line.
* This is pretty simple.
* 2. There is not enough room in this textblock for the
* changed line. This requires a textblock split,
* as with AddLine. There's a lot of common code
* here--should probably consolidate it in a single routine
* some day.
*/
BOOL
ReplaceLine(LineToAdd,CurBlockPtr,CurLinePtr)
TypLine *LineToAdd;
TypBlock far **CurBlockPtr;
TypLine far **CurLinePtr;
{
TypBlock far *MyBlockPtr = *CurBlockPtr;
TypLine far *MyLinePtr = *CurLinePtr;
int deltasize;
int numbytes;
char far *target, far *source;
deltasize = LineToAdd->length - (MyLinePtr->length);
if(deltasize <= ( int)(MyBlockPtr->OwnerDoc->BlockSize - MyBlockPtr->LWAp1) ) {
/* There's room in the current block for this line, so just */
/* move lines down to accomodate this line and copy it in. */
/* Move the data in the textblock up or down, starting with */
/* the line after the line being replaced. */
source = (char far *)MyLinePtr + MyLinePtr->length;
target = source + deltasize;
numbytes = ((char far *)MyBlockPtr+MyBlockPtr->LWAp1) -
(char far *)MyLinePtr - MyLinePtr->length;
#if 0
numbytes = (char far *)MyBlockPtr+MyBlockPtr->LWAp1 - (char far *)MyLinePtr;
target = (char far *)MyLinePtr + deltasize;
#endif
MoveBytes(source,target,numbytes);
MoveBytes((char far *)LineToAdd,(char far *) MyLinePtr,
LineToAdd->length);
MyBlockPtr->LWAp1 += deltasize;
} else {
/* There isn't enough room in the current textblock. */
/* We need to split the textblock. */
/* Find a place to split the block by starting at the */
/* beginning of the block and skipping through the lines */
/* until we pass the number of bytes that marks the */
/* split point. The previous line is the split point. */
TypBlock far *NewBlockPtr;
TypLine far *MyLinePtr = (TypLine far *)((char far *)MyBlockPtr+sizeof(TypBlock));
TypLine far *MyLastLine = MyLinePtr;
int nOldLines = 0, nBytesMoved, MyAddOffset;
#if 0
MessageBox(hWndConf,"Need to split textblock","in ReplaceLine",MB_OK);
#endif
while(((char far *)MyLinePtr - (char far *) MyBlockPtr) < MyBlockPtr->OwnerDoc->SplitSize &&
MyLinePtr->length != END_OF_BLOCK) {
nOldLines++;
MyLastLine = MyLinePtr;
/* (char far *) MyLinePtr += MyLinePtr->length; */
IncPtr(MyLinePtr,MyLinePtr->length);
}
/* Allocate the new block and copy the last portion of */
/* the current block to the new one. The range to */
/* copy starts at the above-determined split point and */
/* goes until the LWA+1. */
/* Then adjust the new & old textblock fields. */
if(NewBlock(MyBlockPtr,&NewBlockPtr)) {
return(1);
} else {
MoveBytes(MyLastLine,(char far *)NewBlockPtr+sizeof(TypBlock),
nBytesMoved = (((char far *)MyBlockPtr + MyBlockPtr->LWAp1)) - (char far *)MyLastLine);
MyBlockPtr->LWAp1 = (char far *)MyLastLine - ((char far *)MyBlockPtr)
+ sizeof(TypLine);
((TypLine far *)MyLastLine)->length = END_OF_BLOCK;
((TypLine far *)MyLastLine)->LineID = NextLineID++;
NewBlockPtr->NumLines = MyBlockPtr->NumLines - nOldLines;
MyBlockPtr->NumLines = nOldLines;
NewBlockPtr->LWAp1 = nBytesMoved + sizeof(TypBlock);
/* Should this line go in the old block or the new? */
/* If the add point is beyond the end of the newly- */
/* truncated block, we must move the add point to the */
/* next block and make the new block the current one. */
/* The new position should be the same */
/* number of line bytes past the beginning of the next */
/* block as it was past the split point when it was in */
/* the old block. */
/* Either way, one block (the old or the new one) */
/* must be unlocked. */
if(*CurLinePtr >= MyLastLine) {
/* Replace point is in new block. */
MyAddOffset = (char far *) *CurLinePtr - (char far *)MyLastLine + sizeof(TypBlock);
*CurLinePtr = (TypLine far *)((char far *)NewBlockPtr + MyAddOffset);
*CurBlockPtr = NewBlockPtr;
GlobalUnlock(MyBlockPtr->hCurBlock);
} else {
/* Add point is in current block. */
GlobalUnlock(NewBlockPtr->hCurBlock);
}
return(ReplaceLine(LineToAdd,CurBlockPtr,CurLinePtr));
}
}
return(TRUE);
}
/*-- function DeleteLine ----------------------------------------------
*
* Delete a line from a textblock.
*
* Entry CurBlockPtr points to the block containing the line.
* CurLinePtr points to the line to delete.
*
* Exit returns TRUE if successful.
* CurBlockPtr points to the block containing the next line,
* if any.
* CurLinePtr points to the next line, if any--else the
* end of the block.
* Usually, CurBlockPtr & CurLinePtr will be the
* same upon exit as upon entry; however, sometimes
* a textblock is emptied and the entire block
* is deleted.
*/
BOOL
DeleteLine(TypBlock far **CurBlockPtr, TypLine far **CurLinePtr)
{
TypBlock far *MyBlockPtr = *CurBlockPtr;
TypLine far *MyLinePtr = *CurLinePtr;
int bytes_to_end, bytes_to_copy;
int cur_length = MyLinePtr->length;
/* If we are (erroneously) at the end of a block, do nothing */
if(MyLinePtr->length == END_OF_BLOCK) {
return(FALSE);
}
/* Copy the remainder of the block on top of the line to be deleted. */
bytes_to_end = ((char far *)MyBlockPtr+MyBlockPtr->LWAp1) -
(char far *) MyLinePtr;
bytes_to_copy = bytes_to_end - cur_length;
MoveBytes((char far *)MyLinePtr+cur_length, (char far *)MyLinePtr,
bytes_to_copy);
/* Update the block counters. */
MyBlockPtr->LWAp1 -= cur_length;
MyBlockPtr->NumLines--;
MyBlockPtr->OwnerDoc->TotalLines--;
/* If we are now at the end of the block, we are faced with one of
* two situations:
* 1. We are also at the beginning of the block, and hence
* the block is empty. In this case we must delete the block
* unless it is the only block.
* 2. Otherwise, we must advance to the next block, if any.
*/
if(MyLinePtr->length == END_OF_BLOCK) {
/* We are at the end of the block. */
if( *((int far *)(MyLinePtr)-1) == END_OF_BLOCK) {
/* We have emptied the block. We must check for whether
* this is the last block in the document.
*/
if(MyBlockPtr->OwnerDoc->TotalLines) {
/* The document is not empty. Delete this empty block.
*/
DeleteBlock(CurBlockPtr,CurLinePtr);
} else {
/* The document is empty. Don't delete this block.
* Leave the pointer at the same place. It is now pointing
* at the next line.
*/
}
} else {
/* We're at the end of the block, but the block is not empty.
* Just advance to the next block, if any.
*/
NextLine(CurBlockPtr,CurLinePtr);
}
}
return(TRUE);
}
/*-- function NextLine ------------------------------------------------
*
* Advance a pointer to point to the next line in a document.
*
* Entry BlockPtr points to the current block.
* LinePtr points to the current line.
*
* Exit BlockPtr & LinePtr point to the next line, if there
* was one.
* Returns TRUE iff pointer was moved.
*
* Method Must advance BlockPtr if LinePtr was at the end
* of a block to start with, or arrives at the end of a block
* after moving to the end of the current line.
*/
int
NextLine(BlockPtr,LinePtr)
TypBlock far **BlockPtr;
TypLine far **LinePtr;
{
BOOL retcode = 0;
if((*LinePtr)->length != END_OF_BLOCK) {
/* (char far *) *LinePtr += (*LinePtr)->length; */
IncPtr(*LinePtr,(*LinePtr)->length);
}
if((*LinePtr)->length == END_OF_BLOCK) {
if( (*BlockPtr)->hNextBlock) {
GlobalUnlock( (*BlockPtr)->hCurBlock);
*BlockPtr = (TypBlock far *) GlobalLock( (*BlockPtr)->hNextBlock);
*LinePtr = (TypLine far *)((char far *)*BlockPtr + sizeof(TypBlock));
retcode = 1;
}
} else {
retcode = 1;
}
return(retcode);
}
/*-- function PrevLine ------------------------------------------------
*
* Back up a pointer to point to the next line in a document.
*
* Entry BlockPtr points to the current block.
* LinePtr points to the current line.
*
* Exit BlockPtr & LinePtr point to the previous line, if there
* was one.
* Returns TRUE iff pointer was moved.
*
* Method Rely on the fact that the last field of the previous
* line is the length of that line. Also, the last field
* in a textblock header (which is what you get if you
* try to look at the previous line in a textblock if you're
* at the beginning of a textblock) has the value END_OF_BLOCK.
*/
int
PrevLine(BlockPtr,LinePtr)
TypBlock far **BlockPtr;
TypLine far **LinePtr;
{
if( *((int far *)(*LinePtr)-1) != END_OF_BLOCK) {
/* (char far *) *LinePtr -= *((int far *)(*LinePtr)-1); */
IncPtr(*LinePtr,-(*((int far *)(*LinePtr)-1)));
} else {
if( (*BlockPtr)->hPrevBlock) {
GlobalUnlock( (*BlockPtr)->hCurBlock);
*BlockPtr = (TypBlock far *) GlobalLock( (*BlockPtr)->hPrevBlock);
*LinePtr = (TypLine far *)
( (char far *)*BlockPtr + (*BlockPtr)->LWAp1 - sizeof(TypLine) );
/* (char far *) *LinePtr -= ( *((int far *)(*LinePtr)-1)); */
IncPtr(*LinePtr,-( *((int far *)(*LinePtr)-1)) );
} else {
return(0);
}
}
return(1);
}
/*--- function TopOfDoc ------------------------------------------------
*
* Set pointers to the first line of a document.
*
* Entry Doc points to a document.
*
* Exit BlockPtr, LinePtr point to the first
* line in the document. This line is locked.
*/
void
TopOfDoc(Doc,BlockPtr,LinePtr)
TypDoc *Doc;
TypBlock far **BlockPtr;
TypLine far **LinePtr;
{
HANDLE hBlock;
int Offset;
TypLineID MyLineID;
hBlock = Doc->hFirstBlock;
Offset = sizeof(TypBlock);
MyLineID = 0L;
LockLine(hBlock,Offset,MyLineID,BlockPtr,LinePtr);
}
/*--- function ExtractTextLine -----------------------------------------------
*
* Extract the text portion of a line to another buffer.
*
* Entry Doc points to the document.
* LinePtr points to a line.
* BufSize is the size of the output buffer in bytes.
*
* Exit Buf contains the text, terminated by a zero byte.
* Returns FALSE iff no text could be extracted
* (not a valid line).
*/
BOOL
ExtractTextLine(Doc,LinePtr,Buf,BufSize)
TypDoc *Doc;
TypLine far *LinePtr;
char *Buf;
int BufSize;
{
char far *bptr;
BOOL DidIt = FALSE;
if(LinePtr->length == END_OF_BLOCK) {
} else {
if(Doc->DocType == DOCTYPE_NET) {
bptr = (char far *)LinePtr + sizeof(TypLine) + sizeof(TypGroup);
while(--BufSize > 1 && (*(Buf++) = *(bptr++)));
*Buf = '\0';
DidIt = TRUE;
} else if(Doc->DocType == DOCTYPE_ARTICLE) {
bptr = (char far *)LinePtr + sizeof(TypLine) + sizeof(TypText);
while(--BufSize > 1 && (*(Buf++) = *(bptr++)));
*Buf = '\0';
DidIt = TRUE;
}
}
return (DidIt);
}
/*-- function LockLine ---------------------------------------------
*
* Find the specified line, and return a pointer to it.
* Lock the line in memory.
*
* Entry: hBlock is the handle of a block we think contains
* the desired line.
* LineOff is the offset in bytes from the beginning of
* the block for where we think the line is.
* FindLineID is the LineID of the desired line.
* If it is 0, we don't check line ID's (don't care).
*
* Exit: returns TRUE iff the line was found.
* BlockPtr points to the beginning of the block
* in which the line was found.
* LinePtr points to the line.
*/
BOOL
LockLine(hBlock,LineOff,FindLineID,BlockPtr,LinePtr)
HANDLE hBlock;
unsigned int LineOff;
TypLineID FindLineID;
TypBlock far **BlockPtr;
TypLine far **LinePtr;
{
*BlockPtr = (TypBlock far *) GlobalLock(hBlock);
*LinePtr = (TypLine far *) ((char far *)*BlockPtr + LineOff);
if(FindLineID && (*LinePtr)->LineID != FindLineID) {
/* The location specified by hBlock and LineOff does not
* contain the right line. So, unlock that block and start
* scanning the document from the top, looking for the line.
*/
TypBlock far *MyBlockPtr;
TypLine far *MyLinePtr;
HANDLE hMyBlock;
int MyOffset;
#if 0
MessageBox(hWndConf,"LineID doesn't match","in LockLine",MB_OK);
#endif
hMyBlock = (*BlockPtr)->OwnerDoc->hFirstBlock;
MyOffset = sizeof(TypBlock);
GlobalUnlock(hBlock);
MyBlockPtr = (TypBlock far *)GlobalLock(hMyBlock);
MyLinePtr = (TypLine far *) ((char far *)MyBlockPtr + sizeof(TypBlock));
while(MyLinePtr->LineID != FindLineID && NextLine(&MyBlockPtr,&MyLinePtr));
if(MyLinePtr->LineID == FindLineID) {
*BlockPtr = MyBlockPtr;
*LinePtr = MyLinePtr;
} else {
MessageBox(hWndConf,"Can't find line","in LockLine",MB_ICONHAND|MB_OK);
return(FALSE);
}
}
return(TRUE);
}
/*-- function UnlockLine ---------------------------------------------
*
* Given a block pointer and a line pointer, unlock the block
* in memory and return a line ID, a block handle and an offset within the
* block to the line.
*
* Entry BlockPtr points to a textblock
* LinePtr points to a line in that textblock
*
* Exit TheLineID is the LineID of the pointed-to line.
* hBlock is the handle to the textblock.
* LineOff is the offset (in bytes from the beginning
* of the block) of the line.
*/
void
UnlockLine(BlockPtr,LinePtr,hBlock,LineOff,TheLineID)
TypBlock far *BlockPtr;
TypLine far *LinePtr;
HANDLE *hBlock;
unsigned int *LineOff;
TypLineID *TheLineID;
{
PtrToOffset(BlockPtr,LinePtr,hBlock,LineOff,TheLineID);
GlobalUnlock(*hBlock);
}
/*-- function PtrToOffset ---------------------------------------------
*
* Given a block pointer and a line pointer,
* return a line ID, a block handle and an offset within the
* block to the line.
*
* Entry BlockPtr points to a textblock
* LinePtr points to a line in that textblock
*
* Exit TheLineID is the LineID of the pointed-to line.
* hBlock is the handle to the textblock.
* LineOff is the offset (in bytes from the beginning
* of the block) of the line.
*/
void
PtrToOffset(BlockPtr,LinePtr,hBlock,LineOff,TheLineID)
TypBlock far *BlockPtr;
TypLine far *LinePtr;
HANDLE *hBlock;
unsigned int *LineOff;
TypLineID *TheLineID;
{
*TheLineID = LinePtr->LineID;
*LineOff = (char far *)LinePtr - (char far *)BlockPtr;
*hBlock = BlockPtr->hCurBlock;
}
/*-- function WhatLine ------------------------------------------------
*
* Determine the ordinal number of a given line in the document.
*
* Entry BlockPtr points to the block containing a line.
* LinePtr points to the line.
*
* Exit Returns 0 = first line, 1 = second, and so on.
*
* Strategy is to start at the beginning of the document and
* scan though the lines, counting lines until we reach the
* current line. In more detail:
*
* Save the current position.
* Go to the first block of the document.
* Number of lines = 0
* While we are not yet at the original block,
* Add in the number of lines in this block, just by looking at header.
* Now that we have reached the original block, start at the
* beginning of the block and scan forward line-by-line until
* we reach the original line.
*/
unsigned int
WhatLine(BlockPtr,LinePtr)
TypBlock far *BlockPtr;
TypLine far *LinePtr;
{
unsigned int nLines = 0;
TypBlock far *MyBlock;
TypLine far *MyLine;
TypDoc *MyDoc;
HANDLE hOrgBlock, hMyBlock, hNewBlock;
unsigned int OrgOffset, MyOffset;
TypLineID OrgLineID, MyLineID=0L;
MyDoc = BlockPtr->OwnerDoc;
UnlockLine(BlockPtr,LinePtr,&hOrgBlock,&OrgOffset,&OrgLineID);
hMyBlock = MyDoc->hFirstBlock;
while(hMyBlock != hOrgBlock) {
MyBlock = (TypBlock far *) GlobalLock(hMyBlock);
nLines += MyBlock->NumLines;
hNewBlock = MyBlock->hNextBlock;
if(!hNewBlock) {
MessageBox(MyDoc->hDocWnd,"Hit end of document","Error in WhatLine",
MB_OK | MB_ICONHAND);
}
GlobalUnlock(hMyBlock);
hMyBlock = hNewBlock;
}
/* The technique of scanning the NumLines field starts the */
/* counter at 1 rather than 0, so if we got any lines above, */
/* adjust the count by decrementing it. */
/* if(nLines) nLines--; */
/* We have reached the original block. */
/* Start scanning at the first line. */
MyOffset = sizeof(TypBlock);
LockLine(hMyBlock,MyOffset,MyLineID,&MyBlock,&MyLine);
while(MyOffset != OrgOffset) {
nLines++;
NextLine(&MyBlock,&MyLine);
MyOffset = (char far *) MyLine - (char far *) MyBlock;
}
return(nLines);
}
/*--- function NumBlocksInDoc --------------------------------------------
*
* Find the number of blocks in a document.
*
* Entry Doc points to a document.
*
* Exit returns the number of textblocks in the document.
*/
int
NumBlocksInDoc(Doc)
TypDoc *Doc;
{
TypBlock far *MyBlock;
HANDLE hMyBlock, hNewBlock;
int nBlocks = 0;
if(!Doc) return(0);
hMyBlock = Doc->hFirstBlock;
do {
nBlocks++;
MyBlock = (TypBlock far *) GlobalLock(hMyBlock);
hNewBlock = MyBlock->hNextBlock;
GlobalUnlock(hMyBlock);
hMyBlock = hNewBlock;
} while (hNewBlock);
return(nBlocks);
}
/*--- function FindLineOrd ---------------------------------------------
*
* Find the Nth line in a document.
*
* Entry Doc points to a document.
* LineOrd is the ordinal of the line to find.
* 0 = first line in document.
*
* Exit BlockPtr points to the block containing the line.
* LinePtr points to the line.
* return value is TRUE iff we found the line.
*/
BOOL
FindLineOrd(Doc,LineOrd,BlockPtr,LinePtr)
TypDoc *Doc;
unsigned int LineOrd;
TypBlock far **BlockPtr;
TypLine far **LinePtr;
{
unsigned int LinesSoFar = 0;
TypBlock far *MyBlockPtr;
TypLine far *MyLinePtr;
HANDLE hBlock,hNextBlock;
int retcode = 0;
hBlock = Doc->hFirstBlock;
do {
MyBlockPtr = (TypBlock far *) GlobalLock(hBlock);
if(LinesSoFar+MyBlockPtr->NumLines > LineOrd) break;
LinesSoFar += MyBlockPtr->NumLines;
hNextBlock = MyBlockPtr->hNextBlock;
GlobalUnlock(hBlock);
hBlock = hNextBlock;
} while(hBlock);
if(hBlock) {
MyLinePtr = (TypLine far *) ((char far *)MyBlockPtr + sizeof(TypBlock));
while(LinesSoFar < LineOrd) {
LinesSoFar++;
if(!NextLine(&MyBlockPtr,&MyLinePtr)) break;
}
retcode = TRUE;
*BlockPtr = MyBlockPtr;
*LinePtr = MyLinePtr;
}
return(retcode);
}
/*-- function MoveBytes -----------------------------------------------
*
* Move a region of bytes in memory from one place to another.
* Handle overlapping regions without destroying the source.
*
* Entry Source points to the FWA of the source.
* Target points to the FWA of the target.
* NumBytes is the number of bytes to copy (>= 0).
*/
void
MoveBytes(FSource,FTarget,NumBytes)
void far *FSource, far *FTarget;
int NumBytes;
{
char far *Source = FSource;
char far *Target = FTarget;
if(Source < Target) {
Source += NumBytes - 1;
Target += NumBytes - 1;
while(NumBytes--) *(Target--) = *(Source--);
} else {
while(NumBytes--) *(Target++) = *(Source++);
}
}
/*--- function InitDoc ---------------------------------------------------
*
* Initialize the fields of a document.
*/
int
InitDoc(Doc,hWnd,Parent,DType)
TypDoc *Doc;
HWND hWnd;
TypDoc *Parent;
int DType;
{
TypBlock far *BlockPtr;
HANDLE hBlock;
Doc->hLastBlock = 0;
Doc->TotalLines = 0;
Doc->ActiveLines = 0;
Doc->BlockSize = BLOCK_SIZE;
Doc->SplitSize = (BLOCK_SIZE*2)/3;
Doc->hDocWnd = hWnd;
Doc->hLastSeenBlock = 0;
Doc->TopLineOrd = 0;
Doc->ParentDoc = Parent;
Doc->ParentLineID = 0L;
Doc->SearchStr[0] = '\0';
Doc->FindLineID = 0L;
Doc->TopScLineID = 0L;
Doc->InUse = TRUE;
Doc->DocType = DType;
Doc->hFindBlock = 0;
switch(DType) {
case DOCTYPE_NET:
Doc->OffsetToText = sizeof(TypLine) + sizeof(TypGroup);
break;
case DOCTYPE_GROUP:
Doc->OffsetToText = sizeof(TypLine) + sizeof(TypArticle);
break;
case DOCTYPE_ARTICLE:
Doc->OffsetToText = sizeof(TypLine);
break;
}
hBlock = GlobalAlloc(GMEM_MOVEABLE,(long)BLOCK_SIZE);
if(hBlock) {
BlockPtr = (TypBlock far *) GlobalLock(hBlock);
SetupEmptyBlock(BlockPtr,hBlock,0,0,Doc);
Doc->hFirstBlock = hBlock;
Doc->hLastBlock = hBlock;
Doc->hCurAddBlock = hBlock;
Doc->AddOffset = sizeof(TypBlock);
Doc->AddLineID = 0L;
Doc->hCurTopScBlock = hBlock;
Doc->TopScOffset = sizeof(TypBlock);
Doc->TopScLineID = 0L;
Doc->LastSeenLineID = 0L;
GlobalUnlock(hBlock);
} else {
MessageBox(hWnd,"Could not allocate textblock","Out of Memory Error",MB_OK|MB_ICONHAND);
}
return(0);
}
/*-- function FreeDoc ----------------------------------------------
*
* Free up all the text blocks associated with a document.
*
* Entry Doc points to the document in question.
*/
void
FreeDoc(Doc)
TypDoc *Doc;
{
TypBlock far *BlockPtr;
HANDLE hBlock, hNextBlock;
/* Start at the first block of the document, and travel */
/* down the linked list of blocks, freeing them. */
hBlock = Doc->hFirstBlock;
while(hBlock) {
BlockPtr = (TypBlock far *) GlobalLock(hBlock);
hNextBlock = BlockPtr->hNextBlock;
GlobalUnlock(hBlock);
GlobalFree(hBlock);
hBlock = hNextBlock;
}
}
/*--- function ForAllLines ---------------------------------------------
*
* Perform an operation for all lines in a document. The operation
* to be performed is specified by a C function argument.
*
* Entry Doc is the document.
* lpfnFunc is a pointer to the function to call for
* each line.
* lFlag is a flag that's passed to the function.
*
*/
void
ForAllLines(Doc,lpfnFunc,wFlag,wValue)
TypDoc *Doc;
void lpfnFunc(TypDoc *Doc, TypBlock far **BlockPtr, TypLine far **LinePtr, int wFlag, int wValue);
int wFlag;
int wValue;
{
TypBlock far *BlockPtr;
TypLine far *LinePtr;
TypLineID old_lineID;
BOOL looping=TRUE;
TopOfDoc(Doc,&BlockPtr,&LinePtr);
if(LinePtr->length != END_OF_BLOCK) {
do {
old_lineID = LinePtr->LineID;
lpfnFunc(Doc,&BlockPtr,&LinePtr,wFlag,wValue);
if(old_lineID == LinePtr->LineID &&
LinePtr->length != END_OF_BLOCK) {
looping = NextLine(&BlockPtr,&LinePtr);
} else if(LinePtr->length == END_OF_BLOCK) {
looping = FALSE;
}
} while(looping);
}
UnlockLine(BlockPtr,LinePtr,
&(FindDoc->hFindBlock),&(FindDoc->FindOffset),&(FindDoc->FindLineID));
}
/*--- function FindString ----------------------------------------------
*
* Locate a search string in a document.
*
* Entry StartAtTop is TRUE iff we should start the search at
* the top of the document.
* FindDoc points to the document in which we are searching.
* ->hFindBlock is the block to start at, if StartAtTop
* is FALSE.
* ->FindOffset is the offset within the block of the
* line to start at, if StartAtTop is FALSE.
* ->SearchStr has the string to search for.
*
* Exit returns -1 if the string was not found,
* else the offset of the string from the beginning of the line.
* FindDoc ...
* ->hFindBlock has the block handle of the line which
* was found (if any)
* ->FindOffset has the offset of the found line (if any)
*
*/
int
FindString(StartAtTop)
BOOL StartAtTop;
{
TypBlock far *BlockPtr;
TypLine far *LinePtr;
HANDLE hBlock;
unsigned int Offset;
unsigned int TextOffset;
BOOL found = -1;
int TargLen=0;
int SourceLen;
char *Target;
char sourceline[MAXINTERNALLINE];
char targline[MAXFINDSTRING];
char *sourceptr, far *orglineptr;
char *targptr;
TypLineID MyLineID;
register char ch;
if(StartAtTop) {
hBlock = FindDoc->hFirstBlock;
Offset = sizeof(TypBlock);
MyLineID = 0L;
} else {
hBlock = FindDoc->hFindBlock;
Offset = FindDoc->FindOffset;
MyLineID = FindDoc->FindLineID;
}
LockLine(hBlock,Offset,MyLineID,&BlockPtr,&LinePtr);
/* If doing a Find Next, skip forward one line before starting */
/* the search. */
if(!StartAtTop) {
NextLine(&BlockPtr,&LinePtr);
}
TextOffset = FindDoc->OffsetToText;
Target = FindDoc->SearchStr;
for(targptr=targline; ch = *Target, *(targptr++)=tolower(ch);
TargLen++) Target++;
if(LinePtr->length != END_OF_BLOCK) {
do {
orglineptr=((char far *)LinePtr)+TextOffset;
sourceptr=sourceline;
for(SourceLen=0; ch = *(orglineptr),
*(sourceptr++)=tolower(ch); SourceLen++) orglineptr++;
found = SearchLine(sourceline,SourceLen,targline,TargLen);
} while(found == -1 && NextLine(&BlockPtr,&LinePtr));
}
UnlockLine(BlockPtr,LinePtr,
&(FindDoc->hFindBlock),&(FindDoc->FindOffset),&(FindDoc->FindLineID));
return(found);
}
/*--- function SearchLine -----------------------------------------------
*
* Search a line for a target string.
*
* Entry Line is a zero-terminated string to search.
* LineLen is strlen(Line). Redundant, but passed for
* efficiency.
* Target is the Target string to search for, zero-terminated.
* TargLen is strlen(Target), passed for efficiency.
*
* Exit returns -1 if not found, else the character position
* in which the string was found (0=first).
*/
int
SearchLine(Line,LineLen,Target,TargLen)
char *Line;
int LineLen;
char *Target;
int TargLen;
{
char *stopptr = Line + LineLen - TargLen + 1;
char *lineptr;
char *searchptr;
char *targptr;
if(LineLen<=0 || TargLen<=0 || TargLen > LineLen) return(-1);
for(lineptr=Line; lineptr != stopptr; lineptr++) {
searchptr = lineptr;
for(targptr = Target; *targptr && *(targptr) == *(searchptr) ;) {
targptr++;
searchptr++;
}
if(!(*targptr)) {
return(lineptr - Line);
}
}
return(-1);
}
/*--- function DoFind ---------------------------------------------------
*
* Controlling routine for searching for text.
* Takes care of displaying window properly when search is done.
*
* Entry StartAtTop is TRUE iff we should start at the top
* of the document.
* FindDoc points to the document being searched.
* All the info we need is in fields in this doc.
*/
BOOL
DoFind(StartAtTop)
BOOL StartAtTop;
{
int CharPos;
int iline;
int X,Y;
HDC hDC;
TypBlock far *BlockPtr;
TypLine far *LinePtr;
HANDLE hBlock;
unsigned int Offset;
char far *textptr;
POINT point;
int mylen;
int found = FALSE;
BOOL refresh = FALSE;
int goline;
unsigned int LineOrd;
unsigned int LastAllowedLineOrd;
TypLineID MyLineID;
CharPos = FindString(StartAtTop);
if(CharPos >= 0) {
iline = LineOnScreen(FindDoc,FindDoc->hFindBlock,FindDoc->FindOffset,FindDoc->FindLineID);
/* If this line wasn't on the screen, we are going to have
* to adjust the top of the screen.
* Make the found line the top of the screen, then back up a
* little to give the user a context in which to view the line.
*/
if(iline == -1) {
FindDoc->hCurTopScBlock = FindDoc->hFindBlock;
FindDoc->TopScOffset = FindDoc->FindOffset;
FindDoc->TopScLineID = FindDoc->FindLineID;
refresh = TRUE;
LockLine(FindDoc->hCurTopScBlock,FindDoc->TopScOffset,FindDoc->TopScLineID,
&BlockPtr,&LinePtr);
for(goline=FindDoc->ScYLines/4; goline; goline--) {
PrevLine(&BlockPtr,&LinePtr);
}
/* Have we gone past the top of the last screen?
* If so, move the top line back to the top of the last screen.
*/
LineOrd = WhatLine(BlockPtr,LinePtr);
LastAllowedLineOrd = FindDoc->TotalLines - FindDoc->ScYLines;
if(LineOrd > LastAllowedLineOrd) {
GlobalUnlock(BlockPtr->hCurBlock);
FindLineOrd(FindDoc,LastAllowedLineOrd,&BlockPtr,&LinePtr);
LineOrd = LastAllowedLineOrd;
}
UnlockLine(BlockPtr,LinePtr,&(FindDoc->hCurTopScBlock),
&(FindDoc->TopScOffset),&(FindDoc->TopScLineID));
FindDoc->TopLineOrd = LineOrd;
iline = LineOnScreen(FindDoc,FindDoc->hFindBlock,FindDoc->FindOffset,FindDoc->FindLineID);
}
#if 0
LockLine(FindDoc->hFindBlock,FindDoc->FindOffset,FindDoc->FindLineID,
&BlockPtr,&LinePtr);
textptr = (char far *) LinePtr + FindDoc->OffsetToText;
hDC = GetDC(FindDoc->hDocWnd);
SelectObject(hDC,hFont);
point.x = SideSpace + LOWORD(GetTextExtent(hDC,textptr,CharPos));
ReleaseDC(FindDoc->hDocWnd,hDC);
UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
point.y = TopSpace + iline * LineHeight + 4*LineHeight/5;
ClientToScreen(FindDoc->hDocWnd,&point);
SetCursorPos(point.x,point.y);
#endif
InvalidateRect(FindDoc->hDocWnd,NULL,FALSE);
found = TRUE;
}
return(found);
}